#include <stdlib.h>
#include <time.h>

#include <vector>
#include <deque>

#include <QKeyEvent>
#include <QPainter>

#include <WA_TextEdit.h>

#define BURN_IMG_SIZE (256)

WriteAttackTextEdit::WriteAttackTextEdit( QWidget *parent )  :
	QTextEdit( parent ),
	burnImage( BURN_IMG_SIZE, BURN_IMG_SIZE, QImage::Format_ARGB32 )
{
	isLockdown = false;
	isDestroy = false;	

	// disable drops
	setAcceptDrops( false );

	// this doesn't work...
	//connect( this, SIGNAL(selectionChanged()), this, SLOT(checkSelection()) );

	connect( &burnAnimTimer, SIGNAL(timeout()), this, SLOT(updateBurn()) );
}

void WriteAttackTextEdit::lockDown()
{
	isLockdown = true;	
}

void WriteAttackTextEdit::unlockDown()
{
	isLockdown = false;	
}

bool WriteAttackTextEdit::lockdown()
{
	return isLockdown;
}

void WriteAttackTextEdit::keyPressEvent( QKeyEvent *event )
{
	// Never a reason to paste into
	if ( (event->key() == Qt::Key_V) && (event->modifiers() & Qt::ControlModifier) )
	{
		//printf("DBG: paste attempt\n" );
		event->accept();
	}
	else if ( (event->key() == Qt::Key_Insert) && (event->modifiers() & Qt::ShiftModifier) )
	{
		//printf("DBG: paste attempt\n" );
		event->accept();
	}
	else if (isLockdown)
	{		
		if ( ((event->key() == Qt::Key_C) || (event->key() == Qt::Key_Insert)) && 
			 (event->modifiers() & Qt::ControlModifier) )
		{
			//printf("DBG: copy attempt\n" );
			event->accept();
		}
		else if ( (event->key() == Qt::Key_Insert) && (event->modifiers() & Qt::ShiftModifier) )
		{
			//printf("DBG: copy attempt2\n" );
			event->accept();
		}
		else if ( (event->key() == Qt::Key_Delete) && (event->modifiers() & Qt::ShiftModifier) )
		{
			//printf("DBG: cut attempt2\n" );
			event->accept();
		}
		else if ( (event->key() == Qt::Key_X) && (event->modifiers() & Qt::ControlModifier) )
		{
			//printf("DBG: cut attempt\n" );
			event->accept();
		}
		// Movement keys.. remove the shift modifier
		else if ( ((event->key() == Qt::Key_Up) || 
			       (event->key() == Qt::Key_Down) ||
				   (event->key() == Qt::Key_Left) ||
				   (event->key() == Qt::Key_Right) ||
				   (event->key() == Qt::Key_Home) ||
				   (event->key() == Qt::Key_End) ||
				   (event->key() == Qt::Key_PageUp) ||
				   (event->key() == Qt::Key_PageDown) 
				   ) && (event->modifiers() & Qt::ShiftModifier) )
		{
			// Remove the shift modifier and handle as normal motion
			//printf("DBG: removing shift\n" );		
			//event->modifier &= (!Qt::ShiftModifier);			
			//QTextEdit::keyPressEvent( event );
			QKeyEvent ev2( event->type(), event->key(),
						   event->modifiers() & (!Qt::ShiftModifier),
						   event->text(), event->isAutoRepeat(), event->count() );
			QTextEdit::keyPressEvent( &ev2 );
			
			event->accept();
		}
		else
		{
			// handle as normal, even in lockddown mdoe
			event->ignore();	
			QTextEdit::keyPressEvent( event );
		}
	}
	else
	{
		// handle the event
		event->ignore();	
		QTextEdit::keyPressEvent( event );
	}
		
}

bool WriteAttackTextEdit::canInsertFromMimeData( const QMimeData * ) const
{
	return false;
}

QMimeData *WriteAttackTextEdit::createMimeDataFromSelection () const
{
	if (isLockdown)
	{
		return NULL;
	}
	else
	{
		return QTextEdit::createMimeDataFromSelection();
	}
}


void WriteAttackTextEdit::dragEnterEvent( QDragEnterEvent *event )
{
	// do nothing.. muhahahah
	event->ignore();
}

void WriteAttackTextEdit::dragLeaveEvent( QDragLeaveEvent *event )
{
	event->ignore();
}

void WriteAttackTextEdit::dragMoveEvent( QDragMoveEvent *event )
{
	event->ignore();
}

void WriteAttackTextEdit::paintEvent( QPaintEvent *event )
{
	QTextEdit::paintEvent( event );	
	if (isDestroy)
	{
		QRect r = contentsRect();		
		QPainter painter( viewport() );		

		QPen pen( Qt::red, 5 );
		painter.setPen( pen );

		//painter.drawLine( 0, 0, r.width(), r.height() );
		//painter.drawLine( r.width(), 0, 0, r.height() );		
		

		//painter.setBrush( Qt::blue );
		//painter.drawRect( event->rect() );
		//painter.setBrush( burnImage );
		painter.drawImage( r, burnImage );
	}
}

void WriteAttackTextEdit::destroyText()
{
	// Initialize burn image transparent
	for (int i=0; i < BURN_IMG_SIZE; i++)
	{
		for (int j = 0; j < BURN_IMG_SIZE; j++)
		{
			burnImage.setPixel( i,j, 0x00000000 );											
		}
	}

	// Ignite some pixels
	for (int i=0; i < 20; i++)
	{
		int x = (int)(((float)rand() / (float)RAND_MAX) * BURN_IMG_SIZE);
		int y = (int)(((float)rand() / (float)RAND_MAX) * BURN_IMG_SIZE);
		doBurnPixel( x, y );
	}

	// start the burning
	srand( time( NULL ) );
	burnAnimTimer.start( 100 );

	isDestroy = true;
	update();
}

void WriteAttackTextEdit::doBurnPixel( int x, int y )
{
	BurnPixel bp (x,y);
	
	burnPixels.push_back( bp );

	burnImage.setPixel( x, y, 0xff000000 );
}

void WriteAttackTextEdit::updateBurn()
{
	//printf(" In updateBurn, queue size %d\n", burnPixels.size() );

	if (burnPixels.size()==0) 
	{
		// done..
		burnAnimTimer.stop();
		isDestroy = false;

		// nuke text
		clear();
		
		// notify others
		emit burningDone();
	}
	else
	{
		std::vector<BurnPixel> pixelsToLight;

		// Ignite neighboring pixels
		for (std::deque<BurnPixel>::iterator pi = burnPixels.begin();
			pi != burnPixels.end(); ++pi )
		{
			// Ignite neighboring pixels
			for (int ii=-1; ii <= 1; ++ii)
			{
				for (int jj=-1; jj <=1; ++jj)
				{
					int xx = (*pi).x + ii;
					int yy = (*pi).y + jj;
					
					// not ourself or offscreen
					if ( ((ii!=0)||(jj!=0)) &&
						  (xx >= 0) && (yy >=0) &&
						  (xx < BURN_IMG_SIZE) && (yy < BURN_IMG_SIZE) )
					{
						QRgb pxl = burnImage.pixel( xx, yy );
						
						if (!(pxl & 0xff000000))
						{
							// unlit
							float chance =((float)rand() / (float)RAND_MAX);
							if (chance < 0.2)
							{
								// ignite!!!
								pixelsToLight.push_back( BurnPixel( xx, yy ) );
								
								// mark as burny now
								burnImage.setPixel( xx, yy, 0x01000000 );
							}
						}
					}
				}
			}

		}		

		// ignition
		for (std::vector<BurnPixel>::iterator bpi = pixelsToLight.begin();
			 bpi != pixelsToLight.end(); ++bpi )
		{
			burnPixels.push_back( (*bpi) );
		}

		// color pixels by age			 
		for (std::deque<BurnPixel>::iterator bpi = burnPixels.begin();
			bpi != burnPixels.end(); ++bpi )
		{
			QRgb pxl = burnColor( (*bpi).age );			
			burnImage.setPixel( (*bpi).x, (*bpi).y, pxl );

			// age pixel
			(*bpi).age += 0.01;
		}

		// age off burnt pixels
		while (burnPixels.size() && (burnPixels.front().age > 1.0))
		{
			burnPixels.pop_front();
		}		
	}

	viewport()->update();
	update();
}

#define lerp( t, a, b) ((t*b)+((1.0-t)*a))
QRgb WriteAttackTextEdit::burnColor( float age )
{
	float r,g,b,a, tval;

	// first stage--- heating up
	if (age < 0.3)
	{		

		//125,84,69
		tval = age / 0.3;
		a = lerp( tval, 0.0, 1.0 );
		r = lerp( tval, 1.0, 125/255.0 );
		g = lerp( tval, 1.0, 84/255.0 );
		b = lerp( tval, 1.0, 69/255.0 );
	}
	else if (age > 0.8) 
	{
		// cool down
		tval = (age - 0.8) / 0.2;
		a = 1.0;
		r = lerp( tval, 0.0, 1.0 );
		g = lerp( tval, 0.0, 1.0 );
		b = lerp( tval, 0.0, 1.0 );
	}
	else
	{
		// second stage.. burning
		float aval = 1.0 - ((age - 0.3) / ( 0.5 ));
		a = 0.9;	
		r = aval;
		g = ((float)rand() / (float)RAND_MAX) * r;
		b = 0.0;
	}	

	int aa, rr, gg, bb;
	aa = (int)(a * 255.);
	rr = (int)(r * 255.);
	gg = (int)(g * 255.);
	bb = (int)(b * 255.);	

	// alpha must be nonzero
	if (aa <= 0) aa = 0x01;

	QRgb pxl( (aa << 24 ) |
				(rr << 16 ) |
				(gg << 8 ) |
				(bb << 0 ) ) ;

	//printf("burn %f %f %f %f -- %d %d %d %d -- 0x%08X\n",
			//a,r,g,b, aa,rr,gg,bb, pxl );

	return pxl;

}
